package cn.yunyichina.reservation.component.pay;

import cn.yunyichina.provider.framework.dubbo.service.ReservationService;
import cn.yunyichina.provider.iface.entity.common.Response;
import cn.yunyichina.reservation.common.jackson.Jackson;
import cn.yunyichina.reservation.constant.Config;
import cn.yunyichina.reservation.constant.Interface;
import cn.yunyichina.reservation.constant.PayConst;
import cn.yunyichina.reservation.constant.PayType;
import cn.yunyichina.reservation.util.DateUtil;
import cn.yunyichina.reservation.util.MoneyUtil;
import cn.yunyichina.reservation.util.RandomUtil;
import cn.yunyichina.reservation.util.XMLUtil;
import cn.yunyichina.reservation.vo.OrderVo;
import cn.yunyichina.utils.convert.JsonUtils;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;

/**
 * 微信支付
 * Created by Lullaby on 2016/9/3.
 */
@Component
public class WechatPayService extends BasePayService {

    private static final Logger logger = LoggerFactory.getLogger(WechatPayService.class);

    private static final String RESERVATION_ORDER_DETAIL = "yyt.reservation.order.detail";

    private static final String RESERVATION_ORDER_UPDATE = "yyt.reservation.order.update";

    private static final String ORDER_NAME_PREFIX = "[预约平台]";

    @Resource
    private Properties yamlProperties;

    /**
     * 微信支付业务入口
     *
     * @param params
     * @return
     */
    public WechatPay pay(Map<String, String> params) {
        WechatPay pay = new WechatPay();
        Response serviceResponse = ReservationService.callHumpFromInternal(
                RESERVATION_ORDER_DETAIL, Jackson.getBaseJsonData(params)
        );
        if (serviceResponse == null) {
            pay.setMessage("Service not available.");
        } else if (Interface.SUCCESS.equals(serviceResponse.getResultCode())) {
            OrderVo orderVo = Jackson.getBeanJsonData(serviceResponse.getResult(), OrderVo.class);
            if (orderVo != null) {
                if (checkOrder(orderVo)) {
                    orderVo.setClientIp(params.get(PayConst.CLIENT_IP));
                    Map<String, String> payParams = buildWechatUnifiedOrderParameters(orderVo);
                    logger.info("[Wechat pay parameters] -> {}", Jackson.getBaseJsonData(payParams));
                    try {
                        pay = unifiedorder(payParams);
                    } catch (Exception e) {
                        logger.error("[Wechat pay exception]", e);
                    }
                    logger.info("[Wechat pay return] -> {}", Jackson.getBaseJsonData(pay));
                    if (pay != null &&
                            PayConst.WECHAT_SUCCESS_CODE.equals(pay.getReturnCode()) &&
                            PayConst.WECHAT_SUCCESS_CODE.equals(pay.getResultCode())) {
                        Map<String, String> unified = new HashMap<>();
                        unified.put("id", orderVo.getId());
                        unified.put("pay_order_time", DateUtil.getDateString(DateUtil.YYYYMMDDHHMMSS));
                        ReservationService.callHumpFromInternal(
                                RESERVATION_ORDER_UPDATE, Jackson.getBaseJsonData(unified)
                        );
                        pay.setNonceStr(RandomUtil.getRandomALLCharToUpperCase(32));
                        pay.setTimestamp(String.valueOf(System.currentTimeMillis() / 1000));
                        pay.setSign(getWechatPaySign(buildWechatPayParameters(pay)));
                        pay.setSuccess(true);
                        logger.info("Prepay success.");
                    } else {
                        logger.info("Prepay failed.");
                    }
                } else {
                    pay.setMessage("Order status abnormal.");
                }
            } else {
                pay.setMessage("Order not found.");
            }
        } else {
            pay.setMessage("Service call error.");
        }
        return pay;
    }

    private static final MediaType TEXT = MediaType.parse("text/html; charset=utf-8");

    private WechatPay unifiedorder(Map<String, String> params) throws IOException, DocumentException {
        String requestContent = XMLUtil.buildMapXML(params);
        OkHttpClient client = new OkHttpClient();
        RequestBody body = RequestBody.create(TEXT, requestContent);
        Request request = new Request.Builder().url(yamlProperties.getProperty(Config.WECHAT_PAY_PAYUNIFIED)).post(body).build();
        okhttp3.Response response = client.newCall(request).execute();
        Map<String, String> result = parseResponseString(response.body().string());
        return JsonUtils.parseObject(result, WechatPay.class);
    }

    private Map<String, String> parseResponseString(String content) throws DocumentException {
        Map<String, String> result = new HashMap<>();
        Document doc = DocumentHelper.parseText(content);
        Element root = doc.getRootElement();
        for (Iterator<Element> iterator = root.elementIterator(); iterator.hasNext(); ) {
            Element e = iterator.next();
            result.put(e.getName(), e.getText());
        }
        return result;
    }

    private Map<String, String> buildWechatUnifiedOrderParameters(OrderVo orderVo) {
        Map<String, String> params = new HashMap<>();
        params.put("appid", yamlProperties.getProperty(Config.WECHAT_PAY_APPID));
        params.put("mch_id", yamlProperties.getProperty(Config.WECHAT_PAY_MCHID));
        params.put("nonce_str", RandomUtil.getRandomALLCharToUpperCase(32));
        params.put("body", ORDER_NAME_PREFIX.concat(" - ").concat(orderVo.getMechanismName()).concat(" - ").concat(orderVo.getItemName()));
        params.put("out_trade_no", orderVo.getOrderNo());
        params.put("total_fee", String.valueOf(MoneyUtil.multiply(orderVo.getCost(), 100)));
        params.put("spbill_create_ip", orderVo.getClientIp());
//        params.put("spbill_create_ip", "127.0.0.1");
        params.put("notify_url", yamlProperties.getProperty(Config.WECHAT_PAY_NOTIFYURL));
        params.put("trade_type", PayType.WechatPayType.APP.toString());
        params.put("sign", getWechatPaySign(params));
        return params;
    }

    private Map<String, String> buildWechatPayParameters(WechatPay pay) {
        Map<String, String> params = new HashMap<>();
        params.put("appid", pay.getAppid());
        params.put("partnerid", yamlProperties.getProperty(Config.WECHAT_PAY_MCHID));
        params.put("prepayid", pay.getPrepayId());
        params.put("package", pay.getPackageValue());
        params.put("noncestr", pay.getNonceStr());
        params.put("timestamp", pay.getTimestamp());
        return params;
    }

    private String getWechatPaySign(Map<String, String> params) {
        String[] keys = new String[params.size()];
        params.keySet().toArray(keys);
        Arrays.sort(keys);
        StringBuffer target = new StringBuffer();
        for (String key : keys) {
            String value = params.get(key);
            if (StringUtils.isNotBlank(value)) {
                target.append(key).append("=").append(value).append("&");
            }
        }
        target.append("key=").append(yamlProperties.getProperty(Config.WECHAT_PAY_PAYSECRET));
        return DigestUtils.md5Hex(target.toString());
    }

}
